package org.teremail.delivery;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.teremail.message.Entity;
import org.teremail.message.EntityVisitor;
import org.teremail.message.MessageHeaders;
import org.teremail.message.Multipart;
import org.teremail.message.MultipartEntity;
import org.teremail.message.MultipartMessage;
import org.teremail.message.SimpleEntity;
import org.teremail.message.SimpleMessage;
import org.teremail.store.Store;
import org.teremail.util.IO;

/**
 * Exposes a message as an input stream.
 * 
 * @author Michael Barker
 *
 */
public class InputStreamVisitor implements EntityVisitor {

    private final Store store;
    private final List<InputStream> streams = new ArrayList<InputStream>();

    public InputStreamVisitor(Store store) {
        this.store = store;
    }
    
    private static InputStream stream(byte[] data) {
        return new ByteArrayInputStream(data);
    }

    public InputStream getInputStream() {
        return new SequenceInputStream(Collections.enumeration(streams));
    }
    
    private void visitMultipart(Multipart multipart, String boundaryStr) {
        byte[] boundary = ("--" + boundaryStr).getBytes(IO.US_ASCII);
        byte[] lastBoundary = ("--" + boundaryStr + "--").getBytes(IO.US_ASCII);
        
        streams.add(stream(multipart.getPreamble().getBytes(IO.US_ASCII)));
        streams.add(stream(IO.ENDL));
        
        for (Entity e : multipart) {
            streams.add(stream(boundary));
            streams.add(stream(IO.ENDL));
            streams.add(stream(e.getContentHeader().toString().getBytes(IO.US_ASCII)));
            //streams.add(stream(IO.ENDL));
            streams.add(stream(IO.ENDL));
            e.accept(this);
            streams.add(stream(IO.ENDL));
        }
        
        streams.add(stream(lastBoundary));        
        streams.add(stream(IO.ENDL));
        streams.add(stream(multipart.getEpilogue().getBytes(IO.US_ASCII)));
    }

    public void visitMultipartEntity(MultipartEntity multipartEntity) {
        String boundary = multipartEntity.getContentHeader().getBoundary();
        visitMultipart(multipartEntity, boundary);
    }

    public void visitMultipartMessage(MultipartMessage multipartMessage) {
        MessageHeaders headers = multipartMessage.getHeader();
        String boundary = headers.getBoundary();
        
        streams.add(stream(headers.toString().getBytes(IO.US_ASCII)));
        streams.add(stream(IO.ENDL));
        streams.add(stream(IO.ENDL));
        visitMultipart(multipartMessage, boundary);
    }

    public void visitSimpleEntity(SimpleEntity simpleEntity) {
        streams.add(store.getInputStream(simpleEntity.getContent().getId()));
    }

    public void visitSimpleMessage(SimpleMessage simpleMessage) {
        MessageHeaders headers = simpleMessage.getHeader();
        streams.add(stream(headers.toString().getBytes(IO.US_ASCII)));
        streams.add(stream(IO.ENDL));
        streams.add(stream(IO.ENDL));
        streams.add(store.getInputStream(simpleMessage.getContent().getId()));
    }

}
